package com.anlogic.sdk.bootimage.model;

import com.anlogic.sdk.bootimage.model.options.AuthenticationType;
import com.anlogic.sdk.bootimage.model.options.BootgenOptionHelper;
import com.anlogic.sdk.bootimage.model.options.CheckSumType;
import com.anlogic.sdk.bootimage.model.options.DestDeviceType;
import com.anlogic.sdk.bootimage.model.options.EncryptionType;
import com.anlogic.sdk.bootimage.model.options.IBootgenOption;
import com.anlogic.sdk.bootimage.model.options.PartionType;
import com.anlogic.sdk.bootimage.model.options.PartitionOwner;
import com.anlogic.sdk.utils.FileUtils;
import com.anlogic.sdk.utils.options.ChipType;
import com.anlogic.sdk.utils.options.CpuType;
import com.google.gson.Gson;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class Partition implements IPartitionProperties {
	public static final String EXT_BIT = "bit";

	public static final String EXT_ELF = "elf";

	private static Set<String> properties = new LinkedHashSet<>();

	private static Set<String> noKeyProperties = new LinkedHashSet<>();

	private static Set<String> mpSoCProperties = new LinkedHashSet<>();

	private ChipType chipType;

	private PartionType partitionType;
	
	private String filePath;

	private Map<String, String> propValueMap;

	public static Partition defaultPartition(ChipType chipType) {
		Partition partition = new Partition();
		partition.setChipType(chipType);
		partition.setDefaults();
		return partition;
	}

	public static Partition newPartition(ChipType chipType) {
		Partition partition = defaultPartition(chipType);
		return partition;
	}

	private void setDefaults() {
		setPartitionType(PartionType.DATAFILE);
		setFilePath("");
		setAuthenticationType(AuthenticationType.NONE);
		setEncryptionType(EncryptionType.NONE);
		setCheckSumType(CheckSumType.NONE);
		setPartitionOwner(PartitionOwner.NONE);
		setMpSoCDefaults();
		setAESKeyFile("");
		setSM4KeyFile("");
	}

	private void setMpSoCDefaults() {
		setDestCpuType(ChipType.getDefaultCpu(chipType.getName()));
		setDestDeviceType(DestDeviceType.PS);
	}

	static {
		properties.add("encryption");
		properties.add("authentication");
		properties.add("checksum");
		properties.add("partition_owner");
		properties.add("alignment");
		properties.add("offset");
		properties.add("load");
		properties.add("exec");
		properties.add("aeskeyfile");
		properties.add("sm4keyfile");
		mpSoCProperties.add("destination_cpu");
		mpSoCProperties.add("destination_device");
		properties.addAll(mpSoCProperties);
		properties.addAll(noKeyProperties);
	}

	private Partition() {
		this.propValueMap = new HashMap<>();
	}

	public ChipType getChipType() {
		return this.chipType;
	}

	public void setChipType(ChipType chipType) {
		this.chipType = chipType;
	}

	public void resetForArchitecture() {
		setMpSoCDefaults();
		resetDestDeviceType();
		setCheckSumType(CheckSumType.NONE);
		setPartitionOwner(PartitionOwner.NONE);
	}

	public void resetDestDeviceType() {
		String fileExtension = FileUtils.getFileExtension(this.filePath);
		if ("bit".equals(fileExtension))
			setDestDeviceType(DestDeviceType.PL);
	}

	public PartionType getPartitionType() {
		return this.partitionType;
	}

	public void setPartitionType(PartionType partitionType) {
		this.partitionType = partitionType;
	}

	public String getFilePath() {
		return this.filePath;
	}

	public void setFilePath(String filePath) {
		this.filePath = filePath;
	}

	public AuthenticationType getAuthenticationType() {
		return (AuthenticationType) BootgenOptionHelper.getNonNullById((IBootgenOption[]) AuthenticationType.values(),
				getPropertyValue("authentication"));
	}

	public void setAuthenticationType(AuthenticationType authenticationType) {
		setPropertyValue("authentication", authenticationType.getId());
	}

	public EncryptionType getEncryptionType() {
		return (EncryptionType) BootgenOptionHelper.getNonNullById((IBootgenOption[]) EncryptionType.values(),
				getPropertyValue("encryption"));
	}

	public void setEncryptionType(EncryptionType encryptionType) {
		setPropertyValue("encryption", encryptionType.getId());
	}

	public String getAESKeyFile() {
		return getPropertyValue("aeskeyfile");
	}

	public void setAESKeyFile(String encryptionKeyFile) {
		setPropertyValue("aeskeyfile", encryptionKeyFile);
	}

	public String getSM4KeyFile() {
		return getPropertyValue("sm4keyfile");
	}

	public void setSM4KeyFile(String encryptionKeyFile) {
		setPropertyValue("sm4keyfile", encryptionKeyFile);
	}
	
	public CheckSumType getCheckSumType() {
		return (CheckSumType) BootgenOptionHelper.getNonNullById((IBootgenOption[]) CheckSumType.values(), getPropertyValue("checksum"));
	}

	public void setCheckSumType(CheckSumType checkSumType) {
		setPropertyValue("checksum", checkSumType.getId());
	}
	
	public PartitionOwner getPartitionOwner() {
		return (PartitionOwner) BootgenOptionHelper.getNonNullById((IBootgenOption[]) PartitionOwner.values(), getPropertyValue("partition_owner"));
	}

	public void setPartitionOwner(PartitionOwner partitionOwner) {
		setPropertyValue("partition_owner", partitionOwner.getId());
	}

	public String getAlignment() {
		return getPropertyValue("alignment");
	}

	public void setAlignment(String alignment) {
		setPropertyValue("alignment", alignment);
	}

	public String getOffSet() {
		return getPropertyValue("offset");
	}

	public void setOffSet(String offSet) {
		setPropertyValue("offset", offSet);
	}

	public String getLoad() {
		return getPropertyValue("load");
	}

	public void setLoad(String load) {
		setPropertyValue("load", load);
	}

	public String getExec() {
		return getPropertyValue("exec");
	}

	public void setExec(String exec) {
		setPropertyValue("exec", exec);
	}

	public void setOrgEndian(boolean orgEndian) {
		String value = orgEndian ? "org_endian" : null;
		setPropertyValue("org_endian", value);
	}
	
	public boolean isOrgEndian() {
		return "org_endian".equals(getPropertyValue("org_endian"));
	}
	
	public CpuType getDestCpuType() {
		return CpuType.getTypeByName(chipType, getPropertyValue("destination_cpu"));
	}

	public void setDestCpuType(CpuType destCpuType) {
		setPropertyValue("destination_cpu", destCpuType.getName());
	}

	public DestDeviceType getDestDeviceType() {
		return (DestDeviceType) BootgenOptionHelper.getNonNullById((IBootgenOption[]) DestDeviceType.values(),
				getPropertyValue("destination_device"));
	}

	public void setDestDeviceType(DestDeviceType destDeviceType) {
		setPropertyValue("destination_device", destDeviceType.getId());
	}

	public static List<String> getProperties() {
		return new ArrayList<>(properties);
	}

	public void setPropertyValue(String property, String value) {
		this.propValueMap.put(property, value);
	}
	
	public void deleteProperty(String property) {
		if (this.propValueMap.containsKey(property))
			this.propValueMap.remove(property);
	}

	public String getPropertyValue(String property) {
		String propValue = this.propValueMap.get(property);
		if (propValue == null)
			propValue = "";
		return propValue;
	}

	public boolean hasDefaultValues() {
		if (getPartitionType() != PartionType.DATAFILE)
			return false;
		Partition defaultPartition = defaultPartition(getChipType());
		for (String property : properties) {
			if (!hasDefaultValue(property, defaultPartition))
				return false;
		}
		return true;
	}

	public boolean hasValue(String property) {
		String value = getPropertyValue(property);
		boolean ret = value != null && !value.isEmpty() && !value.equals("none");
		return ret;
	}
	
	public boolean hasDefaultValue(String property) {
		Partition defaultPartition = defaultPartition(getChipType());
		return hasDefaultValue(property, defaultPartition);
	}

	private boolean hasDefaultValue(String property, Partition defaultPartition) {
		String defaultValue = defaultPartition.getPropertyValue(property);
		String value = getPropertyValue(property);
		return defaultValue.equals(value);
	}

	public void editPartition(Partition partition) {
		setPartitionType(partition.getPartitionType());
		setFilePath(partition.getFilePath());
		for (String property : properties)
			setPropertyValue(property, partition.getPropertyValue(property));
	}

	public Partition getCopy() {
		String partitionString = PartitionStringConverter.convertToString(this);
		Partition copy = PartitionStringConverter.convertToPartition(partitionString, getChipType());
		return copy;
	}

	public boolean isFsbl() {
		return (getPartitionType() == PartionType.BOOTLOADER);
	}

	public static boolean isNoKeyProperty(String property) {
		return noKeyProperties.contains(property);
	}

	public static boolean isMpSoCProperty(String property) {
		return mpSoCProperties.contains(property);
	}
	
	public String toJsonString() {
		Gson gson = new Gson();
		return gson.toJson(this);
	}

	public static Partition readJsonString(String json) {
		Gson gson = new Gson();
		return (Partition) gson.fromJson(json, Partition.class);
	}
}
